﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Network.Messages;
using System.Net.Sockets;
using UnityEngine;
//using NLua;
//using UniScene;


#pragma warning disable 0219
#pragma warning disable 0168
#pragma warning disable 0162
#pragma warning disable 0414
namespace Network {

    public delegate void NetMessageHandler( BaseMessage msg );
    public delegate bool NetMessageDefaultHandler( BaseMessage msg );

    // public enums
    public enum ENetCondition : int {
        DEFAULT = 0,
        DISCONN_TO_CONN,
        CONN_TO_DISCONN,
        DISCONN_BY_COMMAND,
        CONN_BY_COMMAND
    }

    public class Networker 
	{
        const float MaxMsgBlockingTime = 5.0f;
        const int MaxMsgBlockingFrame = 60;
		static System.Random m_random = new System.Random();
        static byte[] _EMPTY_BUFFER = new byte[1];
        // connecting / connected -- IP, Port
		public string m_ip = "192.168.1.91";//公用服务器
      
		//public string m_ip = "192.168.20.4";//谢晓波服务群殴
		//public string m_ip = "192.168.20.135";//勾终玉服务群殴
       // public string m_ip = "192.168.20.181";
		public int m_port = 5000;

		// public string m_ip = "192.168.3.145";
		//public int m_port = 5000;
        // lastconn -- IP, Port
		public string m_lastIP = "192.168.1.91";
		public int m_lastPort = 44803;

        private float m_msgBlockingTime = 0;
        private int m_msgBlockingFrame = 0;
        private NetClient m_net = null;
        private int m_msgLoopSkipCount = 0;
        private float m_networkDelayCount = 0;
        private bool m_enableDelay = false;
        private float m_minNetworkDelay = 0.5f;
        private float m_maxNetworkDelay = 0.6f;

        private Byte[] m_serializeBuffer = null;
        private MemoryStream m_serializeStream = null;
        private BinaryWriter m_binaryWriter = null;

        private object m_locker = new object();
        public bool m_connLaterState = false;

        // new state mark
		private ENetCondition m_netCondition = ENetCondition.DEFAULT;
        private bool m_isCommanDisConn = false;                             // mark is disconn by command

        private Dictionary<UInt16, NetMessageHandler> m_msgHandlers;

        public NetMessageDefaultHandler defaultHandler {
            get;
            set;
        }

        static Networker _sharedInstance = null;

        public static Networker Instance {
            get {
                return sharedInstance;
            }
        }

        public static Networker sharedInstance {
            get {
                if ( _sharedInstance == null ) {
                    _sharedInstance = new Networker();
                }
                return _sharedInstance;
            }
        }

        public bool enableDelay {
            get {
                return m_enableDelay;
            }
            set {
                m_enableDelay = value;
            }
        }

        public float minNetworkDelay {
            get {
                return m_minNetworkDelay;
            }
            set {
                m_minNetworkDelay = Math.Min( m_minNetworkDelay, value );
            }
        }

        public float maxNetworkDelay {
            get {
                return m_maxNetworkDelay;
            }
            set {
                m_maxNetworkDelay = Math.Max( m_maxNetworkDelay, value );
            }
        }

        public event Action<NetClient.NetworkState> onStateChanged {
            add {
                m_net.onStateChanged += value;
            }
            remove {
                m_net.onStateChanged -= value;
            }
        }

        public bool hasStateCallBack {
            get {
                return m_net.netStateCallBack;
            }
        }

        public void Initialize() {
            MessageFactory.Initialize();
            m_msgHandlers = new Dictionary<UInt16, NetMessageHandler>();
            m_msgLoopSkipCount = 0;
            m_serializeBuffer = new Byte[Messages.BaseMessage.BYTE_BUFFER_SIZE];
            m_serializeStream = new MemoryStream( m_serializeBuffer );
            m_binaryWriter = new BinaryWriter( m_serializeStream );
            m_net = new NetClient();
            m_net.Initialize();
        }

        public bool LogMessageEnable {
            get {
                return m_net.logMessageEnable;
            }
            set {
                m_net.logMessageEnable = value;
            }
        }

        public bool SetTracingMsgs( String msgs ) {
            return m_net.SetTracingMsgs( msgs );
        }

        public void ClearReceiveMessagePool() {
            if ( m_net != null ) {
                m_net.ClearReceiveMsgQueue();
            }
        }

        public void Uninitialize() {
            m_msgHandlers.Clear();
            m_msgHandlers = null;
            if ( m_net != null ) {
                m_net.Uninitialize();
                m_net = null;
            }
            if ( m_binaryWriter != null ) {
                m_binaryWriter.Close();
                m_binaryWriter = null;
            }
            if ( m_serializeStream != null ) {
                m_serializeStream.Close();
                m_serializeStream = null;
            }
            m_serializeBuffer = null;
        }

        public void SkipMessageLoop( int count = 1 ) {
            m_msgLoopSkipCount += count;
        }

        public int GetSkipCount() {
            return m_msgLoopSkipCount;
        }

        public bool Connect() {
            m_msgLoopSkipCount = 0;
            MessageData.ClearIndexCounter();
            if ( m_net != null ) {
                bool ret = m_net.Connect( m_ip, m_port );
                return ret;
            } else {
                return false;
            }
        }

        #region member access
        public Socket socket {
            get { return m_net.socket; }
            set {
                lock ( m_locker ) {
                    m_net.socket = value;
                }
            }
        }
        public SocketState socketState {
            get {
                return m_net.socketState;
            }
            set {
                lock ( m_locker ) {
                    m_net.socketState = value;
                }
            }
        }
        public SocketError socketError {
            get { return m_net.socketError; }
            set {
                lock ( m_locker ) {
                    m_net.socketError = value;
                }
            }
        }
        #endregion

        #region Sync Conn
        public void Connect( Action<bool> requestCallback ) {
            m_msgLoopSkipCount = 0;
            MessageData.ClearIndexCounter();
            if ( m_net != null ) {
				Debug.Log ("__________________________UConnect"+ m_ip+"   "+m_port);
                m_net.Connect( requestCallback, m_ip, m_port );
            }
        }
	/*	public void Connect( LuaFunction luaCallBack ) {
			Connect (delegate(bool arg1) {
								luaCallBack.Call (arg1);
						});
		}*/

        #endregion

        public void Disconnect( bool notify = true ) {
            if ( m_net != null ) {
                m_net.Close( notify );
            }
            m_isCommanDisConn = true;
        }

        public bool isConnected {
            get { return m_net != null && m_net.isConnected; }
        }

        public void Register( MessageType _netMessageID, NetMessageHandler handler ) {
            UInt16 netMessageID = (UInt16)_netMessageID;
            if ( m_msgHandlers == null ) {
                m_msgHandlers = new Dictionary<UInt16, NetMessageHandler>();
            }
            if ( m_msgHandlers != null ) {
                if ( !m_msgHandlers.ContainsKey( netMessageID ) ) {
                    m_msgHandlers.Add( netMessageID, handler );
                } else {
                    m_msgHandlers[netMessageID] += handler;
                }
            }
        }

        public void UnRegister( MessageType _netMessageID, NetMessageHandler handler ) {
            UInt16 netMessageID = (UInt16)_netMessageID;
            NetMessageHandler h = null;
            if ( m_msgHandlers != null && m_msgHandlers.TryGetValue( netMessageID, out h ) ) {
                h -= handler;
                m_msgHandlers[netMessageID] = h;
                if ( h == null ) {
                    m_msgHandlers.Remove( netMessageID );
                }
            }
        }

        public void SendMessage( Messages.BaseMessage msg, bool increasePackageIndex = true ) {
            if ( m_net.isConnected ) {
                try {
//					Debug.Log("发送消息：" + ObjTool.ToString(msg));
                    MemoryStream m = m_serializeStream;
                    m.Position = 0;
                    BinaryWriter s = m_binaryWriter;
                    UInt16 size = 4;
                    s.Write( size );
                    UInt16 id;
                    id = (UInt16)msg.GetId();
                    s.Write( id );
                    size = (UInt16)msg.Serialize( s );
                    if ( size >= 0 ) {
                        s.Seek( 0, SeekOrigin.Begin );
                        size += (UInt16)MessageData.HeaderBufferSize;
                        s.Write( size );
                        s.Seek( MessageData.HeaderBufferSize, SeekOrigin.Begin );
                    }
                    Byte[] mm = Common.BlockPool.sharedInstance.Allocate( size );
                    if ( mm == null ) {
                        Common.UDebug.LogErrorEx( "BlockPool[{0}] is empty!", size );
						UnityEngine.Debug.Log("BlockPool[{0}] is empty!" + size);
                        return;
                    }
                    Array.Copy( m_serializeBuffer, mm, size );
                    m_net.PostMessage( mm, size );
                } catch ( Exception e ) {
                    Common.ULogFile.sharedInstance.LogExceptionEx(LogFile.Exception, e);
                }
            }
        }

        public void ReflushSendQueue() {
            m_net.ReflushSendQueue();
        }

        public void Update( float deltaTime ) 
        {
            if ( m_enableDelay ) {
                if ( m_networkDelayCount > 0 ) {
                    m_networkDelayCount -= deltaTime;
                    return;
                }
                float rate = (float)m_random.Next( 0, 100 ) / 100.0f;
                m_networkDelayCount = m_minNetworkDelay + rate * ( m_maxNetworkDelay - m_minNetworkDelay );
            }
            m_net.Update( deltaTime );

            if ( m_net.isConnected || m_connLaterState ) {
                CheckNetCondition();
                m_connLaterState = m_net.isConnected;
                if ( m_msgLoopSkipCount > 0 ) {
                    --m_msgLoopSkipCount;
                    return;
                }
                MessageData data = m_net.PeekMessage();
                while ( data != null ) {
                    using ( BinaryReader reader = new BinaryReader( new MemoryStream( data.data ?? _EMPTY_BUFFER ) ) ) 
                    {
                        bool ret = true;
                        BaseMessage m = null;
                        try {
                            m = MessageFactory.Create( (MessageType)data.id, reader );
                            if ( m != null ) {
                                ret = HandleNetMessage( m );
                            }
                        } catch ( Exception ex ) {
                            ret = true;
                            Common.ULogFile.sharedInstance.LogExceptionEx(LogFile.Exception, ex);
                        }
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
                        Common.UDebug.Assert(ret, "服务器处理失败，请检测，协议ID：" + m.GetId().ToString());
#endif
                        if(ret || !m_connLaterState ||
                            m_msgBlockingTime > MaxMsgBlockingTime &&
                            m_msgBlockingFrame > MaxMsgBlockingFrame ) {
                            if ( !ret ) {
                                if ( !m_connLaterState ) {
                                    String errorInfo = String.Format( "Netmsg:{0} not handled, out of blocking time, pop it.", m.GetType().Name );
                                    Common.UDebug.Assert( false, errorInfo );
                                    Common.UDebug.LogErrorEx( errorInfo );
                                }
                            }
                            m_msgBlockingTime = 0;
                            m_msgBlockingFrame = 0;
                            m_net.PopMessage();
                            if ( m_msgLoopSkipCount > 0 ) {
                                return;
                            }
                            data = m_net.PeekMessage();
                        } else {
                            m_msgBlockingTime += deltaTime;
                            ++m_msgBlockingFrame;
                            return;
                        }
                    }
                }
            }
        }

        private NetMessageHandler GetNetMessageHandler( MessageType id ) {
            NetMessageHandler callback;
            if ( m_msgHandlers != null && m_msgHandlers.TryGetValue( (UInt16)id, out callback ) ) {
                return callback;
            }
            return null;
        }

        private bool HandleNetMessage( Messages.BaseMessage msg ) {
            NetMessageHandler callback;
//			Debug.Log ("接收消息："+ObjTool.ToString(msg));
			if (msg.GetId ().Equals (MessageType.MSG_GS2U_AttackHitResult)) {
				int x = 0;		
			}
            if ( m_msgHandlers != null && m_msgHandlers.TryGetValue( (UInt16)msg.GetId(), out callback ) ) {
                callback( msg );
                return true;
            } else {
                if ( msg != null ) {
                    if ( defaultHandler != null && defaultHandler( msg ) ) {
                        return true;
                    }
                } else {
                    Common.UDebug.LogWarning( String.Format( "Unrecognized message: {0}", msg.GetId() ) );
                }
            }
            return false;
        }

        private void CheckNetCondition() {
            if ( m_net.isConnected != m_connLaterState ) {
                if ( m_net.isConnected ) {
                    // dis conn -> conn
                    m_netCondition = ENetCondition.DISCONN_TO_CONN;
                } else {
                    if ( m_isCommanDisConn ) {
						m_netCondition = ENetCondition.DISCONN_BY_COMMAND;
                    } else {
                        // conn -> dis conn
						m_netCondition = ENetCondition.CONN_TO_DISCONN;
                    }
                }
                m_isCommanDisConn = false;

			    GameSig.NetCondition.emit_NetConditionChanged( m_netCondition );
            }
        }
    }

    public class NetMessageReg : IDisposable {
        public static Func<MessageType,
            Network.NetMessageHandler,
            KeyValuePair<MessageType,
            Network.NetMessageHandler>> MakePair = ( k, v ) => new KeyValuePair<MessageType, Network.NetMessageHandler>( k, v );

        static NetMessageHandler _CreateHandle( Delegate func, out Type oArgType ) {
            var pi = func.Method.GetParameters();
            if ( pi.Length != 1 ) {
                oArgType = null;
                return null;
            }
            Type argType = pi[0].ParameterType;
            oArgType = argType;
            if ( !argType.IsSubclassOf( typeof( BaseMessage ) ) ) {
                return null;
            }
            return ( msg ) => {
                func.DynamicInvoke( Convert.ChangeType( msg, argType ) );
            };
        }

        public static KeyValuePair<MessageType, Network.NetMessageHandler> BindDummy<T>() where T : BaseMessage, new() {
            MessageType id = MessageFactory.GetMessageId<T>();
            if ( id == MessageType.MSG_Undefined ) {
                Common.UDebug.LogError( "NetMessage not define: " + typeof( T ).FullName );
            }
            return new KeyValuePair<MessageType, Network.NetMessageHandler>( id, msg => { Common.UDebug.LogErrorEx( msg.GetId().ToString(), " dummy handled!" ); } );
        }

        public static KeyValuePair<MessageType, Network.NetMessageHandler> Bind<T>( Action<T> func ) where T : BaseMessage, new() {
            MessageType id = MessageFactory.GetMessageId<T>();
            if ( id == MessageType.MSG_Undefined ) {
                Common.UDebug.LogError( "NetMessage no define: " + typeof( T ).FullName );
            }
            return new KeyValuePair<MessageType, Network.NetMessageHandler>( id, msg => func( msg as T ) );
        }

        public static KeyValuePair<MessageType, Network.NetMessageHandler> Create( Delegate func ) {
            Type argType;
            var handle = _CreateHandle( func, out argType );
            MessageType id = MessageFactory.GetMessageId( argType );
            if ( id == MessageType.MSG_Undefined ) {
                Common.UDebug.LogError( "NetMessage no define: " + argType.FullName );
            }
            return new KeyValuePair<MessageType, Network.NetMessageHandler>( id, handle );
        }

        private List<KeyValuePair<MessageType, Network.NetMessageHandler>> reg = null;

        public NetMessageReg( MessageType type, Network.NetMessageHandler handler ) {
            if ( handler != null && type != MessageType.MSG_Undefined ) {
                reg = new List<KeyValuePair<MessageType, Network.NetMessageHandler>>();
                reg.Add( new KeyValuePair<MessageType, Network.NetMessageHandler>( type, handler ) );
                Networker.Instance.Register( type, handler );
            }
        }

        public NetMessageReg( params KeyValuePair<MessageType, Network.NetMessageHandler>[] handlers ) {
            if ( handlers != null && handlers.Length > 0 ) {
                reg = new List<KeyValuePair<MessageType, Network.NetMessageHandler>>();
                for ( int i = 0; i < handlers.Length; ++i ) {
                    reg.Add( handlers[i] );
                    Networker.Instance.Register( handlers[i].Key, handlers[i].Value );
                }
            }
        }

        public void Dispose() {
            if ( reg != null ) {
                for ( int i = 0; i < reg.Count; ++i ) {
                    Networker.Instance.UnRegister( reg[i].Key, reg[i].Value );
                }
                reg = null;
            }
        }
    }
}
